﻿using MvvmCross.Platform;
using MvvmCross.Plugins.Messenger;
using Sidvall.LogManager.UI.Messaging;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.LogManager.UI
{
    public partial class SystemContext
    {
        #region Public Members

        #region ServerManagers

        private Sidvall.LogManager.UI.Entities.ServerManagerCollection _ServerManagers;
        public Sidvall.LogManager.UI.Entities.ServerManagerCollection ServerManagers
        {
            get
            {
                return _ServerManagers;
            }
            private set
            {
                _ServerManagers = value;
            }
        }

        #endregion
        #region UserConfiguration

        private Sidvall.LogManager.UI.Entities.UserConfiguration _UserConfiguration;
        public Sidvall.LogManager.UI.Entities.UserConfiguration UserConfiguration
        {
            get
            {
                return _UserConfiguration;
            }
            private set
            {
                _UserConfiguration = value;
            }
        }

        #endregion

        #region LoadServerInfoAsync

        public async Task LoadServerInfoAsync(bool logOn, bool logOff)
        {
            try
            {
                Sidvall.LogManager.SystemContext.Current.UserInfo = null;
                Sidvall.SystemContext.Current.PrincipalManager.InitializeCurrentPrincipal("local", null);
                if (logOn)
                    await LogOnAsync(logOff);
                var tasks = new List<System.Threading.Tasks.Task>();
                tasks.Add(Sidvall.LogManager.SystemContext.Current.LoadUserInfoAsync());
                await System.Threading.Tasks.Task.WhenAll(tasks);
            }
            catch
            {
            }

            // Notify change
            var message = new ServerManagerMessage(this);
            Sidvall.UI.EventManager.Publish(message);
        }

        #endregion
        #region LoadServerManagerAsync

        public async Task<Sidvall.LogManager.UI.Entities.ServerManagerCollection> LoadServerManagerAsync(Sidvall.LogManager.Data.IServerConnection connection)
        {
            Sidvall.LogManager.Data.IServerConnection serverConnection;
            Sidvall.LogManager.UI.Entities.ServerManager item;
            int iMax;

            if (this.ServerManagers == null)
                this.ServerManagers = new Sidvall.LogManager.UI.Entities.ServerManagerCollection();

            // Remove unused ServerManagers
            iMax = this.ServerManagers.Count;
            for (int i = iMax - 1; i >= 0; i--)
            {
                item = this.ServerManagers[i];
                serverConnection = (from o in Sidvall.LogManager.SystemContext.Current.UserConfiguration.ServerConnections.Items
                                    where (o.ServerConnectionId == item.ServerConnectionId) && (o.IsEnabled)
                                    select o).FirstOrDefault();
                if (serverConnection == null)
                    this.ServerManagers.Remove(item);
                else if (!Sidvall.LogManager.SystemContextConfig.Current.EntityFacade.ServerConnectionManager.IsValid(serverConnection))
                    this.ServerManagers.Remove(item);
            }

            // Update ServerManagers
            if ((connection.IsEnabled) && (Sidvall.LogManager.SystemContextConfig.Current.EntityFacade.ServerConnectionManager.IsValid(connection)))
            {
                item = (from o in this.ServerManagers
                        where o.ServerConnectionId == connection.ServerConnectionId
                        select o).FirstOrDefault();
                if (item != null)
                {
                    if (!connection.IsLoaded)
                    {
                        await item.InitializeAsync();
                        connection.IsLoaded = true;
                    }
                }
                else
                {
                    item = Sidvall.LogManager.UI.Entities.ServerManagerFactory.GetServerManager(connection.ServerConnectionId.Value);
                    await item.InitializeAsync();
                    this.ServerManagers.Add(item);
                    connection.IsLoaded = true;
                }
            }

            // Notify change
            var messenger = Mvx.Resolve<IMvxMessenger>();
            var message = new ServerManagerMessage(this);
            messenger.Publish<ServerManagerMessage>(message);

            return this.ServerManagers;
        }

        #endregion
        #region LoadServerManagersAsync

        public async Task<Sidvall.LogManager.UI.Entities.ServerManagerCollection> LoadServerManagersAsync()
        {
            System.Collections.Generic.List<System.Threading.Tasks.Task> tasks;
            Sidvall.LogManager.Data.IServerConnection serverConnection;
            Sidvall.LogManager.UI.Entities.ServerManager item;
            int iMax;

            if (this.ServerManagers == null)
                this.ServerManagers = new Sidvall.LogManager.UI.Entities.ServerManagerCollection();

            // Remove unused ServerManagers
            iMax = this.ServerManagers.Count;
            for (int i = iMax - 1; i >= 0; i--)
            {
                item = this.ServerManagers[i];
                serverConnection = (from o in Sidvall.LogManager.SystemContext.Current.UserConfiguration.ServerConnections.Items
                                    where (o.ServerConnectionId == item.ServerConnectionId) && (o.IsEnabled)
                                    select o).FirstOrDefault();
                if (serverConnection == null)
                    this.ServerManagers.Remove(item);
                else if (!Sidvall.LogManager.SystemContextConfig.Current.EntityFacade.ServerConnectionManager.IsValid(serverConnection))
                    this.ServerManagers.Remove(item);
            }

            // Add ServerManagers
            iMax = Sidvall.LogManager.SystemContext.Current.UserConfiguration.ServerConnections.Items.Count;
            for (int i = 0; i < iMax; i++)
            {
                serverConnection = Sidvall.LogManager.SystemContext.Current.UserConfiguration.ServerConnections.Items[i];
                if (!serverConnection.IsEnabled)
                    continue;
                if (!Sidvall.LogManager.SystemContextConfig.Current.EntityFacade.ServerConnectionManager.IsValid(serverConnection))
                    continue;
                item = (from o in this.ServerManagers
                        where o.ServerConnectionId == serverConnection.ServerConnectionId
                        select o).FirstOrDefault();
                if (item == null)
                {
                    item = Sidvall.LogManager.UI.Entities.ServerManagerFactory.GetServerManager(serverConnection.ServerConnectionId.Value);
                    this.ServerManagers.Add(item);
                    serverConnection.IsLoaded = false;
                }
            }

            // Update ServerManagers
            tasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
            iMax = Sidvall.LogManager.SystemContext.Current.UserConfiguration.ServerConnections.Items.Count;
            for (int i = 0; i < iMax; i++)
            {
                serverConnection = Sidvall.LogManager.SystemContext.Current.UserConfiguration.ServerConnections.Items[i];
                if (!serverConnection.IsEnabled)
                    continue;
                if (!Sidvall.LogManager.SystemContextConfig.Current.EntityFacade.ServerConnectionManager.IsValid(serverConnection))
                    continue;
                item = (from o in this.ServerManagers
                        where o.ServerConnectionId == serverConnection.ServerConnectionId
                        select o).FirstOrDefault();
                if (item != null)
                {
                    if (!serverConnection.IsLoaded)
                    {
                        tasks.Add(item.InitializeAsync());
                        serverConnection.IsLoaded = true;
                    }
                }
            }
            if (tasks.Count > 0)
                await System.Threading.Tasks.Task.WhenAll(tasks).ConfigureAwait(false);

            // Notify change
            var messenger = Mvx.Resolve<IMvxMessenger>();
            var message = new ServerManagerMessage(this);
            messenger.Publish<ServerManagerMessage>(message);

            return this.ServerManagers;
        }

        #endregion
        #region LoadUserConfiguration

        public Sidvall.LogManager.UI.Entities.UserConfiguration LoadUserConfiguration(string content)
        {
            Sidvall.LogManager.Net.Services.V1.UserConfiguration userConfiguration;
            Sidvall.LogManager.UI.Entities.UserConfiguration item;

            userConfiguration = Sidvall.LogManager.SystemContext.Current.LoadUserConfiguration(content);
            item = new Sidvall.LogManager.UI.Entities.UserConfiguration();
            item.Import(userConfiguration, Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.None, null));
            item.AcceptChanges();
            this.UserConfiguration = item;

            return this.UserConfiguration;
        }
        public Sidvall.LogManager.UI.Entities.UserConfiguration LoadUserConfiguration(Sidvall.LogManager.Data.IUserConfiguration configuration)
        {
            Sidvall.LogManager.Net.Services.V1.UserConfiguration dataItem;
            Sidvall.LogManager.UI.Entities.UserConfiguration item;

            dataItem = new Net.Services.V1.UserConfiguration();
            dataItem.Import(configuration, Sidvall.Data.CopySettings.FullCopyCurrent(Sidvall.Data.DataRowContextOption.None, null));
            dataItem = Sidvall.LogManager.SystemContext.Current.LoadUserConfiguration(dataItem);
            item = new Sidvall.LogManager.UI.Entities.UserConfiguration();
            item.Import(dataItem, Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.None, null));
            item.AcceptChanges();
            this.UserConfiguration = item;

            return this.UserConfiguration;
        }

        #endregion
        #region LoadUserConfigurationAsync

        public async Task<Sidvall.LogManager.UI.Entities.UserConfiguration> LoadUserConfigurationAsync()
        {
            Sidvall.LogManager.Net.Services.V1.UserConfiguration userConfiguration;
            Sidvall.LogManager.UI.Entities.UserConfiguration item;

            userConfiguration = await Sidvall.LogManager.SystemContext.Current.LoadUserConfigurationAsync();
            item = new Sidvall.LogManager.UI.Entities.UserConfiguration();
            item.Import(userConfiguration, Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.None, null));
            item.AcceptChanges();
            this.UserConfiguration = item;

            return this.UserConfiguration;
        }

        #endregion
        #region SaveUserConfigurationAsync

        public async Task<Sidvall.LogManager.UI.Entities.UserConfiguration> SaveUserConfigurationAsync(Sidvall.LogManager.UI.Entities.UserConfiguration configuration,
            bool loadServerManagers)
        {
            Sidvall.LogManager.Net.Services.V1.UserConfiguration dataItem;
            Sidvall.LogManager.UI.Entities.UserConfiguration item;

            dataItem = new Net.Services.V1.UserConfiguration();
            dataItem.Import(configuration, Sidvall.Data.CopySettings.FullCopyCurrent(Sidvall.Data.DataRowContextOption.None, null));
            dataItem = await Sidvall.LogManager.SystemContext.Current.SaveUserConfigurationAsync(dataItem);
            item = GetItem(dataItem, configuration);
            this.UserConfiguration = item;
            if (loadServerManagers)
            {
                await LoadServerManagersAsync();
            }

            return this.UserConfiguration;
        }

        #endregion

        #endregion
        #region Private Members

        #region GetItem

        private static Sidvall.LogManager.UI.Entities.UserConfiguration GetItem(Sidvall.LogManager.Data.IUserConfiguration dataItem, Sidvall.LogManager.UI.Entities.UserConfiguration configuration)
        {
            Sidvall.LogManager.UI.Entities.UserConfiguration item;

            item = new Sidvall.LogManager.UI.Entities.UserConfiguration();
            item.Import(dataItem, Sidvall.Data.CopySettings.FullCopyAll(Sidvall.Data.DataRowContextOption.None, null));
            item.AcceptChanges();

            return item;
        }

        #endregion
        #region LogOnAsync

        private async Task LogOnAsync(bool logOff)
        {
            if (string.IsNullOrWhiteSpace(Sidvall.LogManager.Security.SecurityManager.AuthorizeEndpoint))
                return;
            if ((logOff) || (!await Sidvall.SystemContext.Current.AuthenticationStrategyManager.NewAuthenticationHeaderFromRefreshTokenAsync()))
            {
                var response = await Sidvall.UI.SystemContext.Current.DialogManager.LogOnAsync(Sidvall.LogManager.Security.SecurityManager.AuthorizeEndpoint,
                    Sidvall.LogManager.Configuration.SettingsManager.STSClientIdResolved,
                    Sidvall.LogManager.Security.SecurityManager.ResponseType,
                    Sidvall.LogManager.Security.SecurityManager.Scope,
                    Sidvall.LogManager.Security.SecurityManager.ClientRedirectAddress,
                    Sidvall.LogManager.Configuration.SettingsManager.TwoFactorProviderResolved,
                    null,
                    Sidvall.LogManager.Security.SecurityManager.LogOffEndpoint,
                    null,
                    Sidvall.LogManager.Security.SecurityManager.AuthorityTokenEndpoint,
                    Sidvall.LogManager.Configuration.SettingsManager.STSClientSecretResolved);
                if (response?.TokenResponse != null)
                {
                }
                else
                {
                    throw new Sidvall.Security.UnauthorizedException();
                }
            }
        }

        #endregion

        #endregion
    }
}